﻿/*
 * GLContainerBase.h
 *
 *  Created on: Jun 16, 2014
 *      Author: yuliang
 */

#ifndef GLCONTAINERBASE_H_
#define GLCONTAINERBASE_H_

#include <vector>
#include "common/PLog.h"
#include "GLEntityBase.h"
#include "GLBaseDefine.h"
#include "GLGoodsBase.h"

/*
 * 基础容器
 */
class CGLContainerBase : public CGLEntityBase {
private:
	#define CHECK_CONTAINER_BOX(box) { \
		if (box > m_BoxValidSize || box == 0 || box >= MAX_CONTAINER_BOX_SIZE) { \
			LOG_ERROR("error|container|check|index|box|" << (uint32_t)box << "|valid|" << (uint32_t)m_BoxValidSize); \
			return; \
		} \
	}
public:
	typedef std::vector<CGLGoodsBase*>			VecGoods_t;
	/* must map */
	typedef std::map<Index_t, CGLGoodsBase*>	MapIndexNeedGoods_t;
	typedef std::map<Type_t, MapIndexNeedGoods_t> MapTypeNeedGoods_t;
public:
	CGLContainerBase(UserId_t uid, Type_t type);
	virtual ~CGLContainerBase();

	virtual inline void Reset() { CGLEntityBase::Reset(); m_Uid = 0; m_IndexNeedGoods.clear(); }

	inline bool SetStatus(Status_t status) { return CGLEntityBase::SetStatus(status); }
	inline bool RmStatus(Status_t status) { return CGLEntityBase::RmStatus(status); }
	inline bool ExistStatus(Status_t status) const { return CGLEntityBase::ExistStatus(status); }

	inline void SetOwner(CGLEntityBase* owner) { SetParent(owner); }
	inline CGLEntityBase* GetOwner() const { return GetParent(); }
	inline void SetOther(CGLEntityBase* other) { SetAttachment(other); }
	inline CGLEntityBase* GetOther() const { return static_cast<CGLEntityBase*>(GetAttachment()); }

	inline const UserId_t& GetUserId() const { return m_Uid; }
	inline void SetUserId(const UserId_t& uid) { m_Uid = uid; }

	inline Type_t GetContainerType() const { return m_ContainerType; }
	inline void SetValidBoxSize(uint8_t size) { if (size <= m_BoxTotalSize) m_BoxValidSize = size; }
	inline uint8_t GetValidBoxSize() const { return m_BoxValidSize; }
	inline uint8_t GetTotalBoxSize() const { return m_BoxTotalSize; }
	int32_t InitBox(uint8_t size);

	int32_t ExpandBox(uint8_t size);
	inline int32_t EmptyIndexSize() const {
		if (m_BoxTotalSize == m_BoxValidSize)
			return GetEmptyIndexSize();
		return emptyIndexSize();
	}
	inline bool IsValidBoxNum(uint8_t box) const { return m_BoxValidSize >= box; }
	bool IsEnough(const GoodsInfo& info) const;
	int32_t AutoGoods(GoodsInfo& info);
	int32_t SetGoods(uint8_t box, Index_t index, GoodsInfo& info);
	int32_t AddGoods(uint8_t box, Index_t index, GoodsInfo& info);
	inline int32_t SetGoods(Index_t index, GoodsInfo& info) { return setGoods(index, info); }
	CGLGoodsBase* GetGoods(uint8_t box, Index_t index) const;
	inline CGLGoodsBase* GetGoods(Index_t index) const { return dynamic_cast<CGLGoodsBase*>(GetChild(index)); }
	template<typename V, class T>
	void GetGoods(uint8_t box, V& goods) const;
	inline const VecEntity_t* GetGoods() const { return GetChilds(); }
	int32_t RmGoods(Index_t index);
	int32_t RmGoods(uint8_t box, Index_t index);
	int32_t RmNumGoods(Index_t index, Number_t num);
	int32_t RmNumGoods(uint8_t box, Index_t index, Number_t num);
	int32_t UseGoods(uint8_t box, Index_t index, Value_t value);
	int32_t SwapGoods(uint8_t firstBox, Index_t firstIndex, uint8_t secondBox, Index_t secondIndex);

	static inline Index_t transformIndex(uint8_t box, Index_t index) {
		return (box - 1) * MAX_CONTAINER_BOX_INDEX_SIZE + index; }
	static inline void transformIndex(Index_t index, uint8_t& box, Index_t& boxIndex) {
		if (0 == index) {
			box = 1;
			boxIndex = 0;
		} else if (index < MAX_CONTAINER_BOX_INDEX_SIZE) {
			box = 1;
			boxIndex = index;
		} else {
			box = index / MAX_CONTAINER_BOX_INDEX_SIZE + 1;
			boxIndex = index % MAX_CONTAINER_BOX_INDEX_SIZE;
		}
	}

	virtual bool DecodeContainerExtend(const std::string& info) { return false; }
	virtual bool EncodeContainerExtend(std::string& info) { return true; }
	virtual bool DecodeGoodsExtend(uint8_t box, Index_t index, const std::string& info) { return false; }
	virtual bool EncodeGoodsExtend(CGLGoodsBase*, std::string& info) { return true; }
protected:
	// 新建物品,并初始化
	virtual CGLGoodsBase* getGoods() { return new CGLGoodsBase; }
	virtual void setGoods(CGLGoodsBase* goods) { delete goods; }
	virtual const GoodsFixInfo* getGoodsFixInfo(Type_t type) const = 0;
private:
	inline void rmGoods(Index_t boxIndex, CGLGoodsBase* goods) { RmChild(boxIndex); setGoods(goods); }
	inline Index_t getMaxIndex() const { return transformIndex(m_BoxValidSize, MAX_CONTAINER_BOX_INDEX_SIZE - 1); }
	inline bool isValidIndex(Index_t index) const { return index <= getMaxIndex(); }
	inline Number_t emptyIndexSize() const;

	int32_t setGoods(Index_t boxIndex, GoodsInfo& info);
	int32_t addGoods(Index_t boxIndex, GoodsInfo& info);

	inline void setIndexNeedGoods(Index_t index, CGLGoodsBase* goods);
	inline Number_t getNeedGoodsNumber(const GoodsInfo& info) const;
	inline void rmIndexNeedGoods(Type_t type, Index_t index);
	inline void swapIndexNeedGoods(CGLGoodsBase* typeFirst, Index_t first,
			CGLGoodsBase* secondType, Index_t second);
	inline MapIndexNeedGoods_t& getNeedGoods(Type_t type);
	inline MapIndexNeedGoods_t* existNeedGoods(Type_t type);
private:
	UserId_t							m_Uid;
	Type_t								m_ContainerType;
	uint8_t								m_BoxTotalSize;
	uint8_t								m_BoxValidSize;
	MapTypeNeedGoods_t					m_IndexNeedGoods;
};

template<typename V, class T>
void CGLContainerBase::GetGoods(uint8_t box, V& goods) const {
	CHECK_CONTAINER_BOX(box);
	for (int8_t i = 0; i != MAX_CONTAINER_BOX_INDEX_SIZE; ++i)
		goods.push_back(dynamic_cast<T*>(GetChild(transformIndex(box, i))));
}

Number_t CGLContainerBase::emptyIndexSize() const {
	Number_t tNum = 0;
	Index_t tMaxIndex = getMaxIndex();
	const SetIndex_t* tAllIndex = GetAllEmptyIndex();
	for (SetIndex_t::const_iterator it = tAllIndex->begin(); it != tAllIndex->end(); ++it) {
		if (*it <= tMaxIndex)
			++tNum;
		else
			break;
	}
	return tNum;
}

void CGLContainerBase::setIndexNeedGoods(Index_t index, CGLGoodsBase* goods) {
	if (goods->NeedNums() > 0) {
		MapIndexNeedGoods_t& tNeed = getNeedGoods(goods->GetGoodsType());
		tNeed[index] = goods;
	}
}

Number_t CGLContainerBase::getNeedGoodsNumber(const GoodsInfo& info) const {
	Number_t tNum = 0;
	MapTypeNeedGoods_t::const_iterator tIter = m_IndexNeedGoods.find(info.m_Type);
	if (tIter != m_IndexNeedGoods.end()) {
		const MapIndexNeedGoods_t& tNeed = tIter->second;
		for (MapIndexNeedGoods_t::const_iterator it = tNeed.begin(); it != tNeed.end(); ++it) {
			if (!isValidIndex(it->first))
				break;
			CGLGoodsBase* tGoods = it->second;
			if (tGoods->GetGoodsInfo().Equal(info)) {
				tNum += tGoods->NeedNums();
				if (tNum >= info.m_Number)
					return tNum;
			}
		}
	}

	return tNum;
}

void CGLContainerBase::rmIndexNeedGoods(Type_t type, Index_t index) {
	MapIndexNeedGoods_t* tNeed = existNeedGoods(type);
	if (nullptr != tNeed)
		tNeed->erase(index);
}

void CGLContainerBase::swapIndexNeedGoods(CGLGoodsBase* goodsFirst, Index_t first,
		CGLGoodsBase* goodsSecond, Index_t second) {
	CGLGoodsBase* tFirstGoods = nullptr;
	CGLGoodsBase* tSecondGoods = nullptr;
	if (nullptr != goodsFirst) {
		MapIndexNeedGoods_t* tNeedFirst = existNeedGoods(goodsFirst->GetGoodsType());
		if (nullptr != tNeedFirst) {
			MapIndexNeedGoods_t::iterator tIter = tNeedFirst->find(first);
			if (tIter != tNeedFirst->end()) {
				tFirstGoods = tIter->second;
				if (tFirstGoods != goodsFirst) {
					tFirstGoods = goodsFirst;
					LOG_ERROR("error|container|swap");
				}
			}
		}
	}
	if (nullptr != goodsSecond) {
		MapIndexNeedGoods_t* tNeedSecond = existNeedGoods(goodsSecond->GetGoodsType());
		if (nullptr != tNeedSecond) {
			MapIndexNeedGoods_t::iterator tIter = tNeedSecond->find(second);
			if (tIter != tNeedSecond->end()) {
				tSecondGoods = tIter->second;
				if (tSecondGoods != goodsSecond) {
					tSecondGoods = goodsSecond;
					LOG_ERROR("error|container|swap");
				}
			}
		}
	}

	if (nullptr != tFirstGoods)
		setIndexNeedGoods(second, tFirstGoods);
	if (nullptr != tSecondGoods)
		setIndexNeedGoods(first, tSecondGoods);
}

CGLContainerBase::MapIndexNeedGoods_t& CGLContainerBase::getNeedGoods(Type_t type) {
	MapTypeNeedGoods_t::iterator tIter = m_IndexNeedGoods.find(type);
	if (tIter != m_IndexNeedGoods.end())
		return tIter->second;

	CGLContainerBase::MapIndexNeedGoods_t tMapNeed;
	m_IndexNeedGoods[type] = tMapNeed;
	return m_IndexNeedGoods[type];
}

CGLContainerBase::MapIndexNeedGoods_t* CGLContainerBase::existNeedGoods(Type_t type) {
	MapTypeNeedGoods_t::iterator tIter = m_IndexNeedGoods.find(type);
	if (tIter != m_IndexNeedGoods.end())
		return &tIter->second;
	return nullptr;
}

#endif /* GLCONTAINERBASE_H_ */
